"use strict";

var $ = require("jquery"),
    vizMocks = require("../../helpers/vizMocks.js"),
    loadingIndicatorModule = require("viz/core/loading_indicator"),
    titleModule = require("viz/core/title"),
    projectionEnginesModule = require("viz/vector_map/projection"),
    controlBarModule = require("viz/vector_map/control_bar"),
    legendModule = require("viz/vector_map/legend"),
    tooltipModule = require("viz/core/tooltip"),
    tooltipViewerModule = require("viz/vector_map/tooltip_viewer"),
    dataSourceModule = require("data/data_source/data_source"),
    exportMenuModule = require("viz/core/export"), //TODO maybe if you test layer - you should create exact layer?
    rendererModule = require("viz/core/renderers/renderer");

require("viz/vector_map/vector_map");

$("#qunit-fixture").append('<div id="container" style="width: 1200px; height: 600px;"></div>');

titleModule.Title = vizMocks.stubClass(titleModule.Title, { });
tooltipModule.Tooltip = vizMocks.stubClass(tooltipModule.Tooltip);
exportMenuModule.ExportMenu = vizMocks.stubClass(exportMenuModule.ExportMenu); //TODO maybe if you test layer - you should create exact layer?
loadingIndicatorModule.LoadingIndicator = vizMocks.stubClass(loadingIndicatorModule.LoadingIndicator);
controlBarModule.ControlBar = vizMocks.stubClass(controlBarModule.ControlBar);
legendModule.LegendsControl = vizMocks.stubClass(legendModule.LegendsControl);
tooltipViewerModule.TooltipViewer = vizMocks.stubClass(tooltipViewerModule.TooltipViewer);

var simpleProjection = projectionEnginesModule.projection({
    aspectRatio: 4 / 3,

    to: function(coordinates) {
        return [
            (coordinates[0] - 200) / 200,
            (coordinates[1] - 150) / 150
        ];
    },

    from: function(coordinates) {
        return [
            (coordinates[0] + 1) * 200,
            (coordinates[1] + 1) * 150
        ];
    }
});

var createData = function(featureType, items) {
    return {
        type: "FeatureCollection",
        features: $.map(items, function(item) {
            return {
                type: "Feature",
                geometry: {
                    type: featureType,
                    coordinates: item.coordinates ? item.coordinates : item
                },
                properties: item.properties || {}
            };
        })
    };
};

var environment = {
    beforeEach: function() {
        var renderer = this.renderer = new vizMocks.Renderer();
        rendererModule.Renderer = function() { return renderer; };
    },

    createLayer: function(options) {
        return $("#container").dxVectorMap({
            projection: simpleProjection,
            layers: options
        }).dxVectorMap("instance").getLayers()[0];
    },

    getArea: function(index) {
        return this.renderer.path.getCall(0 + index).returnValue;
    },

    getLine: function(index) {
        return this.renderer.path.getCall(0 + index).returnValue;
    },

    getMarker: function(index) {
        return this.renderer.g.getCall(2 + index).returnValue;
    },

    getLabel: function(index) {
        return this.renderer.text.getCall(index).returnValue;
    }
};

QUnit.module("Elements positioning", environment);

QUnit.test("Areas (Polygon)", function(assert) {
    this.createLayer({
        dataSource: createData("Polygon", [
            [
                [[100, 50], [200, 50], [200, 200], [100, 200]]
            ],
            [
                [[200, 100], [400, 0], [400, 300]],
                [[0, 0], [0, 300], [400, 300], [400, 0]]
            ],
            []
        ])
    });

    assert.strictEqual(this.getArea(0).attr.getCall(1).args[0]["class"], "dxm-area", "type");
    assert.deepEqual(this.getArea(0).attr.getCall(0).args, [{
        points: [
            [400, 500, 600, 500, 600, 200, 400, 200]
        ]
    }], "area 1 (simple)");
    assert.deepEqual(this.getArea(1).attr.getCall(0).args, [{
        points: [
            [600, 400, 1000, 600, 1000, 0],
            [200, 600, 200, 0, 1000, 0, 1000, 600]
        ]
    }], "area 2 (complex)");
    assert.deepEqual(this.getArea(2).attr.getCall(0).args, [{
        points: []
    }], "area 3 (degenerate)");
});

QUnit.test("Areas (Multipolygon)", function(assert) {
    this.createLayer({
        dataSource: createData("MultiPolygon", [
            [
                [
                    [[100, 50], [200, 50], [200, 200], [100, 200]]
                ]
            ],
            [
                [
                    [[200, 100], [400, 0], [400, 300]]
                ],
                [
                    [[0, 0], [0, 300], [400, 300], [400, 0]],
                    [[200, 100], [300, 100], [250, 200]]
                ]
            ],
            []
        ])
    });

    assert.strictEqual(this.getArea(0).attr.getCall(1).args[0]["class"], "dxm-area", "type");
    assert.deepEqual(this.getArea(0).attr.getCall(0).args, [{
        points: [
            [400, 500, 600, 500, 600, 200, 400, 200]
        ]
    }], "area 1 (simple)");
    assert.deepEqual(this.getArea(1).attr.getCall(0).args, [{
        points: [
            [600, 400, 1000, 600, 1000, 0],
            [200, 600, 200, 0, 1000, 0, 1000, 600],
            [600, 400, 800, 400, 700, 200]
        ]
    }], "area 2 (complex)");
    assert.deepEqual(this.getArea(2).attr.getCall(0).args, [{
        points: []
    }], "area 3 (degenerate)");
});

QUnit.test("Areas (simple data source)", function(assert) {
    this.createLayer({
        dataSource: [
            {
                coordinates: [
                    [[100, 50], [200, 50], [200, 200], [100, 200]]
                ]
            },
            {
                coordinates: [
                        [[200, 100], [400, 0], [400, 300]],
                        [[0, 0], [0, 300], [400, 300], [400, 0]]
                ]
            },
            { coordinates: [] }
        ]
    });

    assert.strictEqual(this.getArea(0).attr.getCall(1).args[0]["class"], "dxm-area", "type");
    assert.deepEqual(this.getArea(0).attr.getCall(0).args, [{
        points: [
            [400, 500, 600, 500, 600, 200, 400, 200]
        ]
    }], "area 1 (simple)");
    assert.deepEqual(this.getArea(1).attr.getCall(0).args, [{
        points: [
            [600, 400, 1000, 600, 1000, 0],
            [200, 600, 200, 0, 1000, 0, 1000, 600]
        ]
    }], "area 2 (complex)");
    assert.deepEqual(this.getArea(2).attr.getCall(0).args, [{
        points: []
    }], "area 3 (degenerate)");
});

QUnit.test("Lines (LineString)", function(assert) {
    this.createLayer({
        dataSource: createData("LineString", [
            [[100, 200], [300, 300], [400, 0]],
            []
        ])
    });

    assert.strictEqual(this.getArea(0).attr.getCall(1).args[0]["class"], "dxm-line", "type");
    assert.deepEqual(this.getLine(0).attr.getCall(0).args, [{
        points: [[400, 200, 800, 0, 1000, 600]]
    }], "line 1 (common)");
    assert.deepEqual(this.getLine(1).attr.getCall(0).args, [{
        points: [[]]    // TODO: Investigate
    }], "line 2 (degenerate)");
});

QUnit.test("Lines (MultiPoint)", function(assert) {
    this.createLayer({
        dataSource: createData("MultiPoint", [
            [[100, 200], [300, 300], [400, 0]],
            []
        ])
    });

    assert.strictEqual(this.getArea(0).attr.getCall(1).args[0]["class"], "dxm-line", "type");
    assert.deepEqual(this.getLine(0).attr.getCall(0).args, [{
        points: [[400, 200, 800, 0, 1000, 600]]
    }], "line 1 (common)");
    assert.deepEqual(this.getLine(1).attr.getCall(0).args, [{
        points: [[]]    // TODO: Investigate
    }], "line 2 (degenerate)");
});

QUnit.test("Lines (MultiLineString)", function(assert) {
    this.createLayer({
        dataSource: createData("MultiLineString", [
            [
                [[100, 50], [200, 50], [200, 200], [100, 200]]
            ],
            [
                [[200, 100], [400, 0], [400, 300]],
                [[0, 0], [0, 300], [400, 300], [400, 0]]
            ],
            []
        ])
    });

    assert.strictEqual(this.getArea(0).attr.getCall(1).args[0]["class"], "dxm-line", "type");
    assert.deepEqual(this.getLine(0).attr.getCall(0).args, [{
        points: [
            [400, 500, 600, 500, 600, 200, 400, 200]
        ]
    }], "line 1 (simple)");
    assert.deepEqual(this.getLine(1).attr.getCall(0).args, [{
        points: [
            [600, 400, 1000, 600, 1000, 0],
            [200, 600, 200, 0, 1000, 0, 1000, 600]
        ]
    }], "line 2 (complex)");
    assert.deepEqual(this.getLine(2).attr.getCall(0).args, [{
        points: []
    }], "line 3 (degenerate)");
});

QUnit.test("Lines (simple data source)", function(assert) {
    this.createLayer({
        dataSource: [
            { coordinates: [[100, 200], [300, 300], [400, 0]] },
            { coordinates: [] }
        ]
    });

    assert.strictEqual(this.getArea(0).attr.getCall(1).args[0]["class"], "dxm-line", "type");
    assert.deepEqual(this.getLine(0).attr.getCall(0).args, [{
        points: [
            [400, 200, 800, 0, 1000, 600]
        ]
    }], "line 1 (common)");
    assert.deepEqual(this.getLine(1).attr.getCall(0).args, [{
        points: [
            []  // TODO: Investigate (should be points: [])
        ]
    }], "line 2 (degenerate)");
});

QUnit.test("Markers (Point)", function(assert) {
    this.createLayer({
        dataSource: createData("Point", [
            [0, 100],
            [200, 200],
            []
        ])
    });

    assert.strictEqual(this.getMarker(0).attr.getCall(1).args[0]["class"], "dxm-marker", "type");
    assert.deepEqual(this.getMarker(0).attr.getCall(0).args, [{ translateX: 200, translateY: 400 }], "Marker 1 (common)");
    assert.deepEqual(this.getMarker(1).attr.getCall(0).args, [{ translateX: 600, translateY: 200 }], "Marker 2 (common)");
    assert.deepEqual(this.getMarker(2).attr.getCall(0).args, [{ translateX: NaN, translateY: NaN }], "Marker 3 (degenerate)");
});

QUnit.test("Markers (simple data source)", function(assert) {
    this.createLayer({
        dataSource: [
            { coordinates: [0, 100] },
            { coordinates: [200, 200] },
            { coordinates: [] }
        ]
    });

    assert.strictEqual(this.getMarker(0).attr.getCall(1).args[0]["class"], "dxm-marker", "type");
    assert.deepEqual(this.getMarker(0).attr.getCall(0).args, [{ translateX: 200, translateY: 400 }], "Marker 1 (common)");
    assert.deepEqual(this.getMarker(1).attr.getCall(0).args, [{ translateX: 600, translateY: 200 }], "Marker 2 (common)");
    assert.deepEqual(this.getMarker(2).attr.getCall(0).args, [{ translateX: NaN, translateY: NaN }], "Marker 3 (degenerate)");
});

QUnit.test("Area labels", function(assert) {
    this.renderer.bBoxTemplate = { x: 0, y: -15, width: 40, height: 20 };
    this.createLayer({
        dataSource: createData("Polygon", [
            {
                coordinates: [
                        [[200, 100], [400, 100], [400, 150], [300, 200]]
                ],
                properties: { text: "Item 1" }
            },
            {
                coordinates: [
                        [[100, 50], [100, 150], [200, 150], [200, 50], [100, 50]],
                        [[200, 200], [200, 220], [240, 220], [240, 200]]
                ],
                properties: { text: "Item 2" }
            },
            {
                coordinates: [
                    [[100, 100], [300, 100], [100, 100]]    // T344899
                ],
                properties: { text: "Item 3" }
            },
            {
                coordinates: [],    // T344899
                properties: { text: "Item 4" }
            }
        ]),
        label: {
            enabled: true,
            dataField: "text"
        }
    });

    assert.deepEqual(getLabelPosition(this.getLabel(0)), [853, 353], "label 1");
    assert.deepEqual(getLabelPosition(this.getLabel(1)), [500, 400], "label 2");
    assert.deepEqual(getLabelPosition(this.getLabel(2)), [NaN, NaN], "label 3 (degenerate)");
    assert.deepEqual(getLabelPosition(this.getLabel(3)), [NaN, NaN], "label 4 (degenerate)");

    function getLabelPosition(label) {
        var arg = label.attr.getCall(1).args[0];
        return [Math.round(arg.translateX), Math.round(arg.translateY)];
    }
});

QUnit.test("Line labels", function(assert) {
    this.createLayer({
        dataSource: createData("LineString", [
            {
                coordinates: [[100, 200], [300, 300], [400, 0]],
                properties: { text: "Item 1" }
            },
            {
                coordinates: [],    // T344899
                properties: { text: "Item 2" }
            }
        ]),
        label: {
            enabled: true,
            dataField: "text"
        }
    });

    assert.deepEqual(getLabelPosition(this.getLabel(0)), [842, 125], "label 1");
    assert.deepEqual(getLabelPosition(this.getLabel(1)), [NaN, NaN], "label 2 (degenerate)");

    function getLabelPosition(label) {
        var arg = label.attr.getCall(1).args[0];
        return [Math.round(arg.translateX), Math.round(arg.translateY)];
    }
});

QUnit.module("Layers management", {
    beforeEach: function() {
        var renderer = this.renderer = new vizMocks.Renderer();
        rendererModule.Renderer = function() { return renderer; };
    },

    createLayers: function(options) {
        return $("#container").dxVectorMap({
            layers: options
        }).dxVectorMap("instance");
    }
});

QUnit.test("Array option", function(assert) {
    var map = this.createLayers([
        { name: "layer-a" },
        {},
        { name: "layer-b" },
        {}
    ]);

    var layers = map.getLayers();
    assert.strictEqual(layers.length, 4, "count");
    assert.strictEqual(layers[0].name, "layer-a", "layer 1 name");
    assert.strictEqual(layers[1].name, "map-layer-1", "layer 2 name");
    assert.strictEqual(layers[2].name, "layer-b", "layer 3 name");
    assert.strictEqual(layers[3].name, "map-layer-3", "layer 4 name");
});

QUnit.test("Object option", function(assert) {
    var map = this.createLayers({
        name: "layer"
    });

    var layers = map.getLayers();
    assert.strictEqual(layers.length, 1, "count");
    assert.strictEqual(layers[0].name, "layer", "layer 1 name");
});

QUnit.test("Empty option", function(assert) {
    var map = this.createLayers();

    assert.deepEqual(map.getLayers(), []);
});

// DEPRECATED_15_2
QUnit.test("Deprecated options", function(assert) {
    var map = $("#container").dxVectorMap({
        mapData: [],
        markers: []
    }).dxVectorMap("instance");

    var layers = map.getLayers();
    assert.strictEqual(layers.length, 2, "count");
    assert.strictEqual(layers[0].name, "areas", "layer 1 name");
    assert.strictEqual(layers[1].name, "markers", "layer 2 name");
});

QUnit.test("Change option - increase layers count", function(assert) {
    var map = this.createLayers([
        { name: "layer-1" },
        { name: "layer-2" }
    ]);

    map.option("layers", [
        {},
        {},
        { name: "layer-3" }
    ]);

    var layers = map.getLayers();
    assert.strictEqual(layers.length, 3, "count");
    assert.strictEqual(layers[0].name, "layer-1", "layer 1 name");
    assert.strictEqual(layers[1].name, "layer-2", "layer 2 name");
    assert.strictEqual(layers[2].name, "layer-3", "layer 3 name");
});

QUnit.test("Change option - decrease layers count", function(assert) {
    var map = this.createLayers([
        { name: "layer-1" },
        { name: "layer-2" },
        { name: "layer-3" }
    ]);

    map.option("layers", [
        {},
        {}
    ]);

    var layers = map.getLayers();
    assert.strictEqual(layers.length, 2, "count");
    assert.strictEqual(layers[0].name, "layer-1", "layer 1 name");
    assert.strictEqual(layers[1].name, "layer-2", "layer 2 name");
});

QUnit.test("Get layer by name", function(assert) {
    var map = this.createLayers([
        { name: "layer-a" },
        {},
        { name: "layer-b" }
    ]);

    var layers = map.getLayers();
    assert.strictEqual(map.getLayerByName("layer-a"), layers[0], "layer 1");
    assert.strictEqual(map.getLayerByName("map-layer-1"), layers[1], "layer 2");
    assert.strictEqual(map.getLayerByName("layer-b"), layers[2], "layer 3");
    assert.strictEqual(map.getLayerByName("test"), null, "unknown name");
});

QUnit.test("Get layer by index", function(assert) {
    var map = this.createLayers([
        {},
        {},
        {}
    ]);

    var layers = map.getLayers();
    assert.strictEqual(map.getLayerByIndex(0), layers[0], "layer 1");
    assert.strictEqual(map.getLayerByIndex(1), layers[1], "layer 2");
    assert.strictEqual(map.getLayerByIndex(2), layers[2], "layer 3");
    assert.strictEqual(map.getLayerByIndex(3), null, "not valid index");
});

QUnit.test("Change layer name", function(assert) {
    var map = this.createLayers([
        { name: "layer-1" },
        {}
    ]);

    map.option("layers", [
        { name: "layer-2" },
        {}
    ]);

    assert.ok(map.getLayerByName("layer-2"), "get by new name");
    assert.ok(!map.getLayerByName("layer-1"), "get by old name");
    var layers = map.getLayers();
    assert.strictEqual(layers.length, 2, "count");
    assert.strictEqual(layers[0].name, "layer-2", "layer 1 name");
    assert.strictEqual(layers[1].name, "map-layer-1", "layer 2 name");
});

QUnit.test("getDataSource method", function(assert) {
    var map = this.createLayers({
        dataSource: createData("Polygon", [
            [
                    [[100, 50], [200, 50], [200, 200], [100, 200]]
            ],
            [
                    [[200, 100], [400, 0], [400, 300]],
                    [[0, 0], [0, 300], [400, 300], [400, 0]]
            ],
                []
        ])
    });

    assert.ok(map.getLayers()[0].getDataSource() instanceof dataSourceModule.DataSource);
});
